﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace Common
{
    /// <summary>
    /// 自定义计时器(数据锚点)
    /// </summary>
    /// <remarks>
    /// <para>可记录的信息有：</para>
    /// <para>    1.当前时间：当前执行的时间</para>
    /// <para>    2.项目名称：当前运行的项目名称</para>
    /// <para>    3.上下文：每次开启时将会产生一个新的GUID作为分组。在此上下文记录的描点将会分配在同一个组中，便于查看上下文； </para>
    /// <para>    4.业务类型：被苗点的业务类型；</para>
    /// <para>    5.标签顺序：当前描点在此类型中的排行顺序，用于查找整个上下文执行的顺序；</para>
    /// <para>    6.标签名称：当前描点的名称；</para>
    /// <para>    7.总耗时间：从开启到当前描点时总共所用的时间；</para>
    /// <para>    8.当前耗时：从上的描点到当前描点所使用的时间；</para>
    /// <para>
    /// <![CDATA[输入日志结果：<DP 项目名称^当前时间^上下文^业务类型^标签顺序^标签名称^总耗时间^当前耗时/>]]>
    /// </para>
    /// <para>
    /// <![CDATA[正值筛选日志：<DP (.*)\^(.*)\^(.*)\^(.*)\^(.*)\^(.*)\^(.*)\^(.*)/>]]>
    /// </para>
    /// <para>
    /// <![CDATA[正值替换为SQL：Insert Into Tsys_Debug Values('$1',TO_DATE('$2','YYYY-MM-DD HH24:MI:SS'),'$3','$4','$6',$5,$7,$8);]]>
    /// </para>
    /// </remarks>
    /// <example>
    /// <code>
    /// <![CDATA[
    ///    class StopwatchV2Demo
    ///    {
    ///        //简单模式使用
    ///        public void Demo1()
    ///        {
    ///            StopwatchV2 test = new StopwatchV2("话费充值");
    ///            test.OutputEvent += Log.Debug;
    ///            //开启
    ///            test.Start();
    ///
    ///            //1.检查数据合法性
    ///            //代码省略.......
    ///
    ///            //记录锚点
    ///            test.Tag("检查数据合法性");
    ///
    ///            //2.创建订单
    ///            //代码省略.......
    ///
    ///            //记录锚点
    ///            test.Tag("创建订单");
    ///
    ///            //3.支付订单
    ///            //代码省略.......
    ///
    ///            //记录锚点
    ///            test.Tag("支付订单");
    ///
    ///            //关闭
    ///            test.Stop();
    ///        }
    ///
    ///        //安全模式使用
    ///        public void Demo2()
    ///        {
    ///            //安全模式使用，建议用此模式
    ///            //不需要考虑开启、关闭、内存释放等问题
    ///            using (StopwatchV2 test = new StopwatchV2("话费充值"))
    ///            {
    ///                test.OutputEvent += Log.Debug;
    ///
    ///                //1.检查数据合法性
    ///                //代码省略.......
    ///
    ///                //记录锚点
    ///                test.Tag("检查数据合法性");
    ///
    ///                //2.创建订单
    ///                //代码省略.......
    ///
    ///                //记录锚点
    ///                test.Tag("创建订单");
    ///
    ///                //3.支付订单
    ///                //代码省略.......
    ///
    ///                //记录锚点
    ///                test.Tag("支付订单");
    ///            }
    ///        }
    ///    }
    /// ]]>
    /// </code>
    /// </example>
    [DebuggerDisplay("AppName={AppName},GroupName={GroupName},Context={Context}")]
    public class StopwatchV2 : Stopwatch, IDisposable
    {
        /// <summary>
        /// 为了在日志中区分多线程多处使用，
        /// 此时系统指定一个名词
        /// </summary>
        /// <param name="name">计时器名称</param>
        public StopwatchV2(string name)
        {
            GroupName = name;
        }

        static StopwatchV2()
        {
            //AppName = AppDomain.CurrentDomain.BaseDirectory.Substring(AppDomain.CurrentDomain.BaseDirectory.TrimEnd('\\').LastIndexOf('\\')).Trim('\\');
            AppName = Path.GetDirectoryName(AppContext.BaseDirectory);
            //string ssTemp = ConfigProvider.GetAppSetting("StartStopwatch");
            bool start = true;
            //bool.TryParse(ssTemp, out start);
            StartStopwatch = start;
        }
        /// <summary>
        /// 日志处理器
        /// </summary>
        public event LogOutput OutputEvent;

        /// <summary>
        /// 当前标记时间
        /// </summary>
        public double CurrentMilliseconds { get; private set; }

        /// <summary>
        /// 标记数量
        /// </summary>
        private int TagCount { get; set; }

        /// <summary>
        /// 计时器名称
        /// </summary>
        public string GroupName { get; private set; }

        /// <summary>
        /// 上下文编号
        /// </summary>
        public string Context { get; private set; }

        /// <summary>
        /// <para>是否启动，默认为关闭状态。在Config的appSettings中可配置是否启动</para>
        /// <para><![CDATA[配置案例： <add key="StartStopwatch" value="true"/>]]></para>
        /// </summary>
        public static bool StartStopwatch { get; private set; }

        /// <summary>
        /// <para>当前应用名称</para>
        /// <para><![CDATA[从 AppDomain.CurrentDomain.BaseDirectory 中获取]]></para>
        /// </summary>
        public static string AppName { get; private set; }

        /// <summary>
        /// 开启计时器
        /// </summary>
        public new void Start()
        {
            if (!StartStopwatch)
            {
                return;
            }
            base.Start();
            CurrentMilliseconds = 0;
            Context = Guid.NewGuid().ToString("N");
            Tag("Start Stopwatch");
        }

        /// <summary>
        /// 停止计时器
        /// </summary>
        public new void Stop()
        {
            if (!StartStopwatch)
            {
                return;
            }
            if (!base.IsRunning)
            {
                return;
            }
            Tag("Stop Stopwatch");
            //停止计时
            base.Stop();
        }

        /// <summary>
        /// 记录标记
        /// </summary>
        /// <param name="tag"></param>
        public void Tag(string tag)
        {
            if (!StartStopwatch)
            {
                return;
            }
            if (!base.IsRunning)
            {
                Start();
            }
            TagCount += 1;
            //当前时间：当前执行的时间
            //项目名称：当前运行的项目名称
            //上下文：每次开启时将会产生一个新的GUID作为分组。在此上下文记录的描点将会分配在同一个组中，便于查看上下文；
            //类型名称：被苗点的业务类型；
            //标签顺序：当前描点在此类型中的排行顺序，用于查找整个上下文执行的顺序；
            //标签名称：当前描点的名称；
            //总耗时间：从开启到当前描点时总共所用的时间；
            //当前耗时：从上的描点到当前描点所使用的时间；
            string msg = string.Format("<DP {0}^{1}^{2}^{3}^{4}^{5}^{6}^{7}/>",
                AppName,
                DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"),
                Context,
                GroupName,
                TagCount,
                tag,
                base.ElapsedMilliseconds,
                base.ElapsedMilliseconds - CurrentMilliseconds);
            Write(msg);
            CurrentMilliseconds = base.ElapsedMilliseconds;
        }

        private void Write(string info)
        {
            if (OutputEvent == null)
            {
                return;
            }
            OutputEvent(info);
        }

        /// <summary>
        /// 关闭并回收资源
        /// </summary>
        public void Dispose()
        {
            Stop();
        }
    }
    /// <summary>
    /// 日志输出器
    /// </summary>
    /// <param name="info">锚点信息</param>
    public delegate void LogOutput(string info);

    class StopwatchV2Demo
    {
        //简单模式使用
        public void Demo1()
        {
            StopwatchV2 test = new StopwatchV2("话费充值");
            //test.OutputEvent += Log.Debug;
            //开启
            test.Start();

            //1.检查数据合法性
            //代码省略.......

            //记录锚点
            test.Tag("检查数据合法性");

            //2.创建订单
            //代码省略.......

            //记录锚点
            test.Tag("创建订单");

            //3.支付订单
            //代码省略.......

            //记录锚点
            test.Tag("支付订单");

            //关闭
            test.Stop();
        }

        //安全模式使用
        public void Demo2()
        {
            //安全模式使用，建议用此模式
            //不需要考虑开启、关闭、内存释放等问题
            using (StopwatchV2 test = new StopwatchV2("话费充值"))
            {
                //test.OutputEvent += Log.Debug;

                //1.检查数据合法性
                //代码省略.......

                //记录锚点
                test.Tag("检查数据合法性");

                //2.创建订单
                //代码省略.......

                //记录锚点
                test.Tag("创建订单");

                //3.支付订单
                //代码省略.......

                //记录锚点
                test.Tag("支付订单");
            }
        }
    }
}
